package org.je.xml.stream;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * fan yi feng
 * 
 * @author fyf
 */
class ToXml
{

	public static final String	Map			= "1";
	public static final String	Collection	= "2";

	String toXml(Object bean) {
		if (bean == null) {
			return "<null/>";
		}

		OID idCreater = new OID();

		ObjCache refMap = new ObjCache();

		StringBuilder bf = new StringBuilder();

		save(null, bean, refMap, idCreater, bf);

		return bf.toString();
	}

	private void save(String name, Object bean, ObjCache refMap, OID idCreater, StringBuilder bf) {

		String nameAtt = (name == null ? "" : " n='" + name + "'");// nameAtt前面有空格

		if (bean == null) { // bean 为空
			bf.append("<null" + nameAtt + "/>");
		} else // 不为空
		{
			Integer id = refMap.get(bean);
			if (id != null) // 引用已经存在的对象
			{
				bf.append("<r" + nameAtt + " id='" + id + "'/>");
			} else // 说明不是引用已经存在的对象
			{

				Class<?> cl = bean.getClass();
				if (InputStream.class.isAssignableFrom(cl) || OutputStream.class.isAssignableFrom(cl)) {
					return;
				}

				id = idCreater.get();

				/************************ 保存起来 *************************/
				refMap.save(bean, id);

				if (TypeUtil.isBase(cl)) // 基本来行
				{
					base2xml(nameAtt, bean, id, bf); //

				} else if (TypeUtil.isStructure(cl))// 集合类型
				{
					stc2xml(nameAtt, bean, refMap, id, bf, idCreater); //

				} else if (TypeUtil.isArray(cl)) // 数组类型
				{
					array2xml(nameAtt, bean, refMap, id, bf, idCreater);

				} else // 一般对象类型
				{
					bf.append("<obj t='" + StrMap.map1(bean.getClass().getName()) + "'" + nameAtt + " id='" + id + "'>");

					for (Field fb : TypeUtil.getFields(cl, true)) {
						try {
							Object fValue = fb.get(bean);
							save(fb.getName(), fValue, refMap, idCreater, bf);
						} catch (Exception ex) {
							throw new RuntimeException(ex);
						}
					}

					bf.append("</obj>");
				}
			}
		}
	}

	private void base2xml(String nameAtt, Object bean, int id, StringBuilder bf) {
		Class<?> cl = bean.getClass();

		String value = null;

		if (cl == String.class) {
			value = (String) bean;
			if (checkStr(value)) {
				value = "<![CDATA[" + value + "]]>";
			}
		} else if (cl == Date.class) {
			value = ((Date) bean).getTime() + "";
		} else {
			value = bean.toString();
		}

		bf.append("<b t='" + StrMap.map1(cl.getName()) + "'" + nameAtt + " id='" + id + "'>" + value + "</b>");
	}

	private void stc2xml(String nameAtt, Object bean, ObjCache map, int id, StringBuilder bf, OID idCreater) {
		Class<?> cl = bean.getClass();

		if (TypeUtil.isMap(cl)) // map类型
		{
			map2xml(nameAtt, bean, map, id, bf, idCreater);

		} else if (TypeUtil.isCollection(cl)) // 一般集合类型
		{
			collection2xml(nameAtt, bean, map, id, bf, idCreater);
		}
	}

	@SuppressWarnings("rawtypes")
	private void map2xml(String nameAtt, Object bean, ObjCache map, int id, StringBuilder bf, OID idCreater) {
		Class<?> cl = bean.getClass();

		bf.append("<map t='" + cl.getName() + "'" + nameAtt + " id='" + id + "' type='" + Map + "'>");

		Map tem = (Map) bean;

		for (Object en : tem.entrySet()) {
			Map.Entry ey = (Map.Entry) en;
			bf.append("<e><k>");
			Object key = ey.getKey();
			save(null, key, map, idCreater, bf);
			bf.append("</k>");
			bf.append("<v>");
			Object value = ey.getValue();
			save(null, value, map, idCreater, bf);
			bf.append("</v></e>");

		}
		bf.append("</map>");
	}

	@SuppressWarnings("rawtypes")
	private void collection2xml(String nameAtt, Object bean, ObjCache map, int id, StringBuilder bf, OID idCreater) {
		Class<?> cl = bean.getClass();

		String tag = (List.class.isAssignableFrom(cl) ? "list" : "set");

		bf.append("<" + tag + " t='" + cl.getName() + "'" + nameAtt + " id='" + id + "' type='" + Collection + "'>");

		for (Object obj : (Collection) bean) {
			this.save(null, obj, map, idCreater, bf);
		}

		bf.append("</" + tag + ">");
	}

	private void array2xml(String nameAtt, Object bean, ObjCache map, int id, StringBuilder bf, OID idCreater) {
		Class<?> cl = bean.getClass();
		Object[] arr = copClassAndLevel(cl);

		Class<?> copCl = (Class<?>) arr[0];
		int level = (Integer) arr[1];
		int length = java.lang.reflect.Array.getLength(bean);

		bf.append("<arr" + " t='" + StrMap.map1(copCl.getName()) + "'" + nameAtt + " id='" + id + "' level='" + level
				+ "'>");

		for (int i = 0; i < length; ++i) {
			Object obj = java.lang.reflect.Array.get(bean, i);

			save(null, obj, map, idCreater, bf);
		}

		bf.append("</arr>");
	}

	private Object[] copClassAndLevel(Class<?> cl) {
		Object[] arr = new Object[2];
		int c = 0;
		Class<?> tem = cl;
		while (true) {
			arr[0] = tem;
			tem = tem.getComponentType();
			if (tem == null) {
				break;
			}
			++c;
		}
		arr[1] = c;
		return arr;
	}

	private boolean checkStr(String msg) {
		for (int i = 0; i < msg.length(); ++i) {
			char c = msg.charAt(i);
			if (c == '>' || c == '<') {
				return true;
			}
		}
		return false;
	}

	private static class ObjCache
	{

		private final List<Obj>	list	= new ArrayList<Obj>();

		public void save(Object obj, Integer i) {
			Obj o = new Obj();
			o.obj = obj;
			o.id = i;
			list.add(o);
		}

		public Integer get(Object obj) {
			for (Obj o : list) {
				if (o.obj == obj) {
					return o.id;
				}
			}
			return null;
		}

		private static class Obj
		{

			Object	obj;
			Integer	id;
		}
	}

	static class OID
	{
		private int	num;

		int get() {
			return ++num;
		}
	}
}
